<?php
/**
 * This file is part of DomBankLink.
 * 
 * DomBankLink is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomBankLink is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomBankLink.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Mihkel Jõhvik
 * @package DomBankLink
 */

/**
 * @author Mihkel Jõhvik
 * @package DomBankLink
 */
final class DomBankLink_Paypal extends DomBankLink {
	
	/**
	 * The PayPal account of the reseller
	 * @var String
	 */
	protected $resellerAccount;
	
	/**
	 * Character encoding used in the transaction
	 * @var String
	 */
	protected $charset = 'UTF-8';
	
	/**
	 * Specifies the method by which the user is redirected to the handleReply method
	 * 0 - GET method
	 * 1 - GET method, no transaction details
	 * 2 - POST method, all transaction details
	 * @var Int
	 */
	protected $returnMethod = 2;
	
	/**
	 * Specifies the language for the log-in and billing pages at paypal.com
	 * Two character string denoting the language (Language Code, ie. US)
	 * @var String
	 */
	protected $lang = US;
	
	/**
	 * Specifies which button the user clicked. Allowed values:
	 * _xclick - direct payment
	 * _donations - donation
	 * _xclick-subscriptions - subscription 
	 * _oe-gift-certificate - buy gift certificate
	 * _s-xclick - secure direct payment
	 * @var String
	 */
	protected $cmd = '_xclick';
	
	/**
	 * Specifies whether the shipping info is required or not 
	 * 0 - asked for, but not required
	 * 1 - not asked for
	 * 2 - asked for and required
	 * @var Int
	 */
	protected $shipping = 1;
	
	/**
	 * Where the payment info will be sent
	 * @var String
	 */
	protected $serverUrl;
	
	/**
	 * Authentication token from a Paypal account. Used to identify yourself when sending PDT requests
	 * @Var String
	 */
	protected $authToken;
	
	public function getName() {
		return 'Paypal';
	}
	
	public function getKey() {
		return 'Paypal';
	}
	
	public function getIconUrl() {
		return DOM_URL . '../libsPub/DomBankLink/images/Paypal.gif';
	}
	
	public function sendToBank( DomBankLinkObject $payment ) {
		
		if( $payment instanceof DomBankPayment ) {
			$rsField = array(
				'amount'    	=> $payment->price,
				'currency_code' => strtoupper($payment->currency),
				'business'      => $this->resellerAccount,
				'item_number'   => $payment->id,
				'item_name'     => $payment->description,
				'return'     	=> $this->getCallBackUrl( $payment ),
				'cancel_return' => $this->getCallBackUrl( $payment ),
				'rm'			=> $this->returnMethod,
				'lc'			=> $this->lang,
				'charset'		=> $this->charset,
				'cmd'			=> $this->cmd,
			);
		}
        	
    	$fields = '';
    	foreach ($rsField as $ixField => $fieldValue) {
    		$fields .= '<input type="hidden" name="' . $ixField . '" value="' . htmlspecialchars($fieldValue) . '" />' . "\n";
    	}

    	$form = '
			<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
			<html xmlns="http://www.w3.org/1999/xhtml">
			<head>
			<meta http-equiv="Content-Type" content="text/html; charset='.$this->charset.'" />
			<title>Suunamine</title>
			</head>
			<body>
			<form id="form" action="'.$this->serverUrl.'" method="POST">
				'.$fields.'
				<input type="submit" value="Checkout"/>
			</form>
			<script type="text/javascript"> t = setTimeout( "document.getElementById(\"form\").submit()" ,10 ); </script>	
			</body>
			</html>';
    	
    	echo $form;
		die();
	}
	
	public function handleReplay( $paymentClassName, $requestId = NULL ) {
		
		$rsField = array();
		
		foreach ((array)$_REQUEST as $ixField => $fieldValue) {
			$rsField[$ixField] = $fieldValue;
		}
		
		$payment = DomAr::load( (int)$rsField['item_number'], $paymentClassName );
	    
	    if( !$payment )
			return false;

		$type = $this->checkMessageType( $rsField );	
		
		if( $type == 1 ) {
			$this->processPDT( $rsField['tx'], $payment );
		} elseif( $type == 2) {
			$this->processIPN( $payment, $rsField );
		}
		
		if( !$payment->save() ) {
			throw new RuntimeException( $payment->getValidationMessages('<br>') );
		}
		
		return $payment;
	}
	
	/**
	 * Checks whether the reply came via PDT or IPN
	 */
	public function checkMessageType( $rsField ) {
		if( $rsField['tx'] && !empty($rsField['tx']) ) return 1;
		if( $rsField['txn_id'] && !empty($rsField['txn_id']) ) return 2;
		return false;
	}
	
	/**
	 * Preforms the checks and actions necessary to process a message that came via PDT
	 */
	public function processPDT( $tx, $payment ) {
		if( $payment->status == 0 ) {
			$result = $this->confirmPDTInfo( $tx );
			//if info is confirmed by Paypal
			if( $result ) {
				//if email is correct and tx hasn't been used before
				if( $this->confirmInfo( $result['txn_id'], $result['receiver_email'], $payment ) ) {
					$payment = $this->processPayment( $payment, $result );
				} else {
					$payment->status = 3;
				}
			} else {
				$payment->status = 3;
			}
		}
		return $payment;
	}
	
	/**
	 * Preforms the checks and actions necessary to process a message that came via IPN
	 */
	public function processIPN( $payment, $result ) {
		$this->logIPN( $result );
		//if info is confirmed by Paypal
		if( $this->confirmIPNInfo() ) {
			if( $payment->status == 4 ) {
				//if email is correct and tx hasn't been used before
				if( $this->confirmInfo( $result['txn_id'], $result['receiver_email'], $payment ) ) {
					$payment = $this->processPayment( $payment, $result );
				} else {
					$payment->status = 3;
				}
			}
		} else {
			$payment->status = 3;
		}
		return $payment;
	}
	
	/**
	 * Logs IPN messages
	 */
	public function logIPN( $result ) {
		$log = new DomBankLink_PaypalIpnLog();
		foreach ($result as $key => $value) {
			$log->post .= '|'.addslashes($key).' - '.addslashes($value);
		}
		$log->save();
	}
	
	/**
	 * Confirms that the account the payment was made to was this account and that tx has never been used before
	 */
	public function confirmInfo( $tx, $email, $payment ) {
		
		$txCheck = DomBankLink_PaypalTxLog::dataQuery()->select('t.tx')->where('t.tx = \''.addslashes($tx).'\'')->commit()->toArray();
		
		if( !empty($txCheck) ) {
			return false;
		} else {
			$txlog = new DomBankLink_PaypalTxLog();
			$txlog->payment = $payment->id;
			$txlog->tx = $tx;
			$txlog->save();
		}
		
		if( $email != $this->resellerAccount ) 
			return false;
		
		return true;
	}
	
   /**
	* Confirms that the PDT info originates from paypal
	*/
	public function confirmPDTInfo( $tx ) {
		
		$post = 'cmd=_notify-synch&tx='.$tx.'&at='.$this->authToken;
		
		$curl = curl_init ($this->serverUrl);
		curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt ($curl, CURLOPT_HEADER, 0);
		curl_setopt ($curl, CURLOPT_TIMEOUT, 30);
		curl_setopt ($curl, CURLOPT_POST, 1);
		curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt ($curl, CURLOPT_POSTFIELDS, $post);
		
		$result = curl_exec ($curl);
		
		if (curl_errno($curl) != 0)
			return false;
		
		curl_close($curl);	
			
		$lines = split("\n", $result);
		
		if ($lines[0] == "SUCCESS") {
			$info = array();
			foreach($lines as $i) {
				$parts = split("=", $i);
				if (count($parts)==2) {
					$info[$parts[0]] = urldecode($parts[1]);
				}
			}
			return $info;
		} else {
			return false;
		}
	}
	
	/**
	 * Confirms that the IPN info originates from Paypal
	 * MUST BE CALLED WHETHER YOU NEED TO PROCESS THE INFO OR NOT,
	 * otherwize Paypal keeps sending the info
	 */
	public function confirmIPNInfo() {
		
		$info = 'cmd=_notify-validate';
	
		foreach ( $_POST as $key => $value ) {
			$value = urlencode(stripslashes($value));
			$info .= '&'.$key.'='.$value;
		}
		
		$curl = curl_init();
		curl_setopt($curl, CURLOPT_URL, $this->serverUrl);
		curl_setopt($curl, CURLOPT_FAILONERROR, 1);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER,1);
		curl_setopt($curl, CURLOPT_TIMEOUT, 30);
		curl_setopt($curl, CURLOPT_POST, 1);
		curl_setopt($curl, CURLOPT_POSTFIELDS, $info);
		
		$result = curl_exec($curl);
		
		if (curl_errno($curl) != 0)
			return false;
		
		curl_close($curl);
		
		if ( strcmp($result, "VERIFIED" ) == 0) {
			return true;
		} else {
		    return false;
		} 
	}
	
	public function processPayment( $payment, $result ) {
		
		if ( $result['payment_status'] == "Failed" || $result['payment_status'] == "Denied" ){
			$payment->status = 2;
		} else if ( $result['payment_status'] == "Completed" ) {
			$payment->status = 1;
		} else if( $result['payment_status'] == "Pending" ) {
			$payment->status = 4;
		} else {
			$payment->status = 3;
		}
			
		$payment->statusDescription = $result['payment_status'];
		$payment->backTime = 'now';
		$payment->backIp = ip();
		
		$payment->clientAccountNr = $result['payer_id'];
		$payment->clientName = strtoupper($result['first_name'].' '.$result['last_name']);
		
		foreach( $result as $key => $i ) {
			$paymentInfo .= $key.': '.$i.'\r\n';
		}
		
		$payment->paymentInfo = $paymentInfo;
		
		return $payment;
	}
}
?>